#coding:utf-8
"""
BeautifulSoup--------CSS选择器

    与lxml一样,BeautifulSoup也是一个HTML/XML的解析器,主要功能是如何解析和提取HTML/XML

    lxml只会局部遍历,而BeautifulSoup是基于HTML DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都比较大,性能低于lxml
    
    BeautifulSoup将复杂HTML文档转换成一个复杂的属性结构,每个节点都是python对象,所有对象可以归纳为4种:Tag  NavigableString  BeautifulSoup  Comment

    ps: 在抓取解析时,解析速度最快的是  正则--lxml--BeautifulSoup
    
"""


import requests, re

from bs4 import BeautifulSoup

html = """
    <html><head><title>The Dormouse's story</title></head>
    <body>
    <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
    <p class="story">Once upon a time there were three little sisters;
    and their names were
    <a href="http://example.com/elsie" class="sister" id="link1"><!-- E
    lsie --></a>,
    <a href="http://example.com/lacie" class="sister" id="link2">Lacie<
    /a> and
    <a href="http://example.com/tillie" class="sister" id="link3">Tilli
    e</a>;
    and they lived at the bottom of a well.</p>
    <p class="story">...</p>
    """

# 创建BeautifulSoup对象, 这里最好指定lxml,不然解析器将会自己选择本系统中最快的解析器lxml,但是如换了系统或环境,就不保证了
soup = BeautifulSoup(html, 'lxml')

# 打开本地html文件的方式
#soup = BeautifulSoup(open('index.html'))

#格式化输出soup对象内容
#print(soup.prettify())

#---------------------------------Tag 标签 / BeautifulSoup-------------------------------
# 我们可以利用 soup 加标签名轻松地获取这些标签的内容，这些对象的类型是bs4.element.Tag。但是注意，它查找的是在所有内容中的第一个符合要求的标签
#输出title标签
print(soup.title)

#输出head标签
print(soup.head)

#输出文档中第一个a标签
print(soup.a)

#输出第一个p标签
print(soup.p)

#Tag标签的属性 name(对应标签名)  attrs(对应标签属性字典)
print(soup.name)  # ------BeautifulSoup对象表示的是一个文档的内容,一般可以当作特殊的Tag对象,文档名就是document
print(soup.head.name)  #-----BeautifulSoup----文档本身属性为空
print(soup.p.name)
print(soup.p.attrs)
print(soup.a.attrs)
print(soup.p['class'])
print(soup.p.get('class'))
del soup.p['class']  #删除属性

#--------------------------------NavigableString 获取标签内部文字------------------------
print("*"*50)
# print(soup.p.string)
# print(type(soup.p.string))

print(type(soup.p.strings), soup.p.strings)  # 拿到一个生成器对象, 取到p标签下下所有的文本内容
for item in soup.p.strings:
    print(item)
    print(1)
print("*"*50)

"""
The Dormouse's story
<class 'bs4.element.NavigableString'>
<generator object _all_strings at 0x7f21c11ff5c8>
<class 'generator'>
"""
#---------------------------------Comment 特殊的NavigableString对象---------------------
print(soup.a)
#这里需注意a标签中含有注释,html中不输出,但.string后注释符号已经去掉,正常输出
print(soup.a.string)
print(type(soup.a.string))

#---------------------------------------------------------------------------------------
#tag.contents 以列表形式输出tag的所有子节点,需注意换行符也是一个节点
print(soup.body.contents)

#tag.children 获取所有字节点,是一个list生成器
print(soup.head.children)
for child in soup.body.children:
    print(child)

#tag.descendants 获取tag所有子孙节点
for child in soup.descendants:
    print(child)

#如果一个标签里只有一个标签,那么这两个标签.string都是最里面的内容
print(soup.head.string)
print(soup.title.string)


#---------------------------------搜索文档树(找多个标签)------------------------------------------
#根据标签
print(soup.find_all('b'))
#根据正则表达似乎
print(soup.find_all(re.compile('^b')))
#根据列表
print(soup.find_all(['a', 'b']))
#根据属性参数
print(soup.find_all(class_='sister'))
print(soup.find_all(id='link2'))
#根据text参数(即文档中的字符串内容)
print(soup.find_all(text="Elsie"))  # text=字符串/列表/正则

#find与find_all用法类型,只是find只返回第一个匹配结果,find_all返回所有匹配结果的列表

# CSS选择器查找,类似find_all,返回所有匹配结果的列表
print(soup.select('title'))  #标签
print(soup.select('.sister'))  #类名
print(soup.select('#link1'))  #id
print(soup.select('p #link1'))  #组合查询
print(soup.select('head > title'))  #直接子标签查找
print(soup.select('a[class="sister"]'))  #属性查找
print(soup.select('.story a[id="link3"]'))  #组合查询

#以上select方法返回的结果都是列表形式,可以遍历输出,然后利用get_text()方法获取
print(soup.select('title')[0].get_text())
for title in soup.select('title'):
    print(title.get_text())
    print(title.string)




